import Tank from "./Tank";
import Direction from "../Direction";
import CollisionGroup from "../CollisionGroup";

const { ccclass, property } = cc._decorator;

@ccclass
export default class TankBullet extends cc.Component {
    @property({
        type: Tank,
    })
    owner: Tank = null;

    private static maxPos;
    private static getMaxPos() {
        if (!this.maxPos) {
            this.maxPos = {
                x: ~~(0.5 * cc.winSize.width),
                y: ~~(0.5 * cc.winSize.height)
            };
        }
        return this.maxPos;
    }

    public static create(owner: Tank) {
        const node = new cc.Node(`bullet_of_tank_${owner.getId()}_${Date.now()}`);
        node.group = CollisionGroup.bullet;

        const bullet = node.addComponent(TankBullet);
        bullet.owner = owner;
        bullet.init();

        cc.find('Canvas').addChild(node);
    }

    onBeginContact(_, __, other: cc.PhysicsBoxCollider) {
        switch (other.node.group) {
            case CollisionGroup.bullet:
                return;
            case CollisionGroup.player:
            case CollisionGroup.enemy:
                if (other.tag === this.owner.getId()) {
                    return;
                }
                break;
            case CollisionGroup.wall:
            default:
                break;
        }
        this.destroy();
    }

    private async init() {
        const owner = this.owner;

        const texture2d = await owner.getBulletTexture2d();
        const size = this.calcBulletSizeByTankLevel(owner.getLevel());

        const sizeRect = cc.size(size, size);

        const node = this.node;

        const sprite = node.addComponent(cc.Sprite);
        sprite.spriteFrame = new cc.SpriteFrame(texture2d);
        sprite.sizeMode = cc.Sprite.SizeMode.CUSTOM;
        sprite.type = cc.Sprite.Type.SIMPLE;
        sprite.node.setContentSize(sizeRect);
        sprite.spriteFrame.setRect(cc.rect(0, 0, size, size));

        const rigidBody = node.addComponent(cc.RigidBody);
        rigidBody.enabledContactListener = true;
        rigidBody.bullet = false;
        rigidBody.type = cc.RigidBodyType.Kinematic;
        rigidBody.fixedRotation = true;
        rigidBody.awakeOnLoad = true;
        rigidBody.allowSleep = true;

        const boxCollider = node.addComponent(cc.BoxCollider);
        boxCollider.size = sizeRect;

        const physicsBoxCollider = node.addComponent(cc.PhysicsBoxCollider);
        physicsBoxCollider.size = sizeRect;
        physicsBoxCollider.sensor = true;
        physicsBoxCollider.restitution = 0;
        physicsBoxCollider.friction = 0;
        physicsBoxCollider.tag = owner.getId();
        physicsBoxCollider.apply();

        this.setInitialPos();
        this.setRigidBodySpeed(rigidBody);
    }
    private setRigidBodySpeed(rigidBody: cc.RigidBody) {
        const speed = 100 + 50 * this.owner.getLevel();
        const position = this.node.getPosition();
        const res = new cc.Vec2(0, 0);
        const maxPos = TankBullet.getMaxPos();
        let moveDistance;
        switch (this.owner.getDirection()) {
            case Direction.up:
                res.y = speed;
                moveDistance = (maxPos.y - position.y);
                break;
            case Direction.right:
                res.x = speed;
                moveDistance = (maxPos.x - position.x);
                break;
            case Direction.down:
                res.y = -speed;
                moveDistance = (maxPos.y + position.y);
                break;
            case Direction.left:
                res.x = -speed;
                moveDistance = (maxPos.x + position.x);
                break;

            default:
                return;
        }
        rigidBody.gravityScale = 0;
        rigidBody.linearVelocity = res;

        const delayTime = moveDistance / speed;
        this.scheduleOnce(this.destroy, Math.ceil(delayTime));
    }
    private setInitialPos() {
        const direction = this.owner.getDirection();
        const currentPos = this.owner.node.position;
        let x = currentPos.x;
        let y = currentPos.y;
        switch (this.owner.getDirection()) {
            case Direction.up:
            case Direction.down:
                const offsetY = ~~(0.5 * this.owner.node.height);
                if (Direction.up === direction) {
                    y += offsetY;
                } else {
                    y -= offsetY;
                }
                break;

            case Direction.right:
            case Direction.left:
                const offsetX = ~~(0.5 * this.owner.node.width);
                if (Direction.right === direction) {
                    x += offsetX;
                } else {
                    x -= offsetX;
                }
                break;

            default:
                return;
        }
        this.node.setPosition(x, y);
    }
    private calcBulletSizeByTankLevel(level: number): number {
        return 2 * (++level);
    }

    public destroy(): boolean {
        this.unscheduleAllCallbacks();
        this.owner = null;
        if (this.node.isValid) {
            this.node.destroy();
        }
        return super.destroy();
    }
}
